#include <unistd.h>
#include <cstring>
#include <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define SIZE 2048
#define FLUSH_NOW 0
#define FLUSH_LINE 2
#define FLUSH_ALL 4

using namespace std;

//其实C语言给我们提供的FILE类型就是封装了很多属性
//而这些属性当中一定有文件描述符(因为他的底层是通过系统调用)和用户及缓冲区
typedef struct MyFile
{
    int fd;//文件描述符
    char buffer[SIZE];//缓冲区大小
    int out_pos;//缓冲区还剩下多少数据n个
    int flag;//这个用来控制刷新方式
}MyFile;

MyFile* _fopen(const char* path, const char* mode)
{
    int _fd = -1;
    if(strcmp(mode, "r") == 0)
    {
        _fd = open(path, O_WRONLY, 0666);
    }
    else if(strcmp(mode, "w") == 0)
    {
        _fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0666);
    }
    else if(strcmp(mode, "a") == 0)
    {
        _fd = open(path, O_WRONLY|O_CREAT|O_APPEND, 0666);
    }
    else
    {
        return nullptr;
    }

    //这一步我忘记了
    if(_fd == -1)
    {
        return nullptr;
    }

    //为我们的结构体创建对象
    MyFile* myfile = (MyFile*)malloc(sizeof(MyFile));
    //malloc太久没用了忘记了判断
    if(myfile == nullptr)
    {
        perror("malloc fail");
        return nullptr;
    }
    myfile->fd = _fd;
    myfile->out_pos = 0;
    myfile->flag = FLUSH_ALL;//默认为0表示为 行刷新

    return myfile;
}

//我才发现原来fflush函数是C语言给我们提供的接口我还以为是系统调用
//其实就是把缓冲区里面的数据给它全部发完
void _fflush(MyFile* stream)
{
    //我忘了：需要先判断缓冲区有没有数据, 如果没有数据就不刷新
    if(stream->out_pos > 0)
    {
        ssize_t n = write(stream->fd, stream->buffer, stream->out_pos);
        //我忘了刷新完成之后继续维护自己的数据
        stream->out_pos = 0;
    }
}

void _fclose(MyFile* stream)
{
    //我忘记了判断指针是否为空
    if(stream == nullptr)
    {
        return;
    }

    //关闭做三件事情 1.通过系统调用关闭文件 2.把自定义结构体给释放
    //我忘记了第三件事情就是刷新
    _fflush(stream);
    close(stream->fd);
    free(stream);
}

int _fwrite(const void* ptr, int len, MyFile* stream)
{
    //第一步应该是把ptr拷贝给由语言维护的缓冲区, 然后来维护语言缓冲区里面的属性参数: fd, buffer, out_pos, flag
    //注意这里的拷贝还不能直接拷贝，否则会覆盖掉之前的数据
    memcpy(&stream->buffer[stream->out_pos], ptr, len);
    stream->out_pos += len;//这里是 += 不是 =

    //第二步数应该判断刷新方式来控制什么事件把数据通过系统调用刷新到内核
    //我们规定0表示立即刷新, 1表示行刷新, 2表示知道缓冲区满了才刷新
    if(stream->flag & FLUSH_NOW)
    {
        write(stream->fd, stream->buffer, stream->out_pos);
        stream->out_pos = 0;
    }
    else if(stream->flag & FLUSH_LINE)
    {
        //我们需要在缓冲区中查找'\n'来判断是否刷新这个想法不对
        //我们只需要判断当前拷贝进来的数据的最后一个元素是不是'\n'
        if(stream->buffer[stream->out_pos - 1] == '\n')
        {
            write(stream->fd, stream->buffer, stream->out_pos);
            stream->out_pos = 0;
        }
    }
    else if(stream->flag & FLUSH_ALL)
    {
        //这里是知道缓冲区满了才刷新
        if(stream->out_pos == SIZE)
        {
            write(stream->fd, stream->buffer, stream->out_pos);
            stream->out_pos = 0;
        }
    }

    return len;
}

